package com.todo.event.activity.application.core.engine.aviator;

import com.alibaba.fastjson.JSONObject;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Options;
import com.todo.event.activity.application.core.engine.aviator.common.SignEnum;
import com.todo.event.activity.application.core.engine.aviator.function.InList;
import com.todo.event.activity.application.core.engine.aviator.function.NotinList;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author azhebuxing
 * @date 2025/1/9 08:05
 * @description
 */
@Slf4j
public class AviatorHelper {

    static {
        AviatorEvaluator.setOption(Options.MATH_CONTEXT, MathContext.DECIMAL128);
        AviatorEvaluator.addFunction(new InList());
        AviatorEvaluator.addFunction(new NotinList());
    }

    /**
     * 验证输入的公式是否合法
     *
     * @param exp 公式表达式
     * @return 是否合法
     */
    public static boolean validateExp(String exp) {
        // 记录每个符号的索引,以及上一个符号的位置
        List<Triple<Integer, Integer, SignEnum>> signIndexList = new ArrayList<>();
        // 判断括号是否对称出现，记录位置，递归处理括号内部的子公式
        Stack<Pair<Integer, SignEnum>> stack = new Stack<>();
        for (int i = 0; i < exp.length(); i++) {
            String ch = String.valueOf(exp.charAt(i));
            SignEnum sign = SignEnum.match(ch);
            if (Objects.isNull(sign)) {
                continue;
            }
            if (sign == SignEnum.LB) {
                stack.push(Pair.of(i, SignEnum.LB));
            } else if (sign == SignEnum.RB) {
                if (stack.isEmpty()) {
                    return false;
                }
                Pair<Integer, SignEnum> lbPair = stack.pop();
                if (lbPair.getLeft() >= (i - 1)) {
                    return false;
                }
                String subFormula = exp.substring(lbPair.getLeft() + 1, i);
                boolean subFormulaValid = validateExp(subFormula);
                if (!subFormulaValid) {
                    return false;
                }
            } else {
                Integer lastSignIndex =
                        signIndexList.isEmpty()
                                ? null
                                : signIndexList.get(signIndexList.size() - 1).getMiddle();
                signIndexList.add(Triple.of(lastSignIndex, i, sign));
            }
        }
        if (!stack.isEmpty()) {
            return false;
        }

        // 没有括号：1. 不能以运算符作为开头和结尾， 2. 运算符不能连续出现
        List<SignEnum> signEnums = SignEnum.noBracketsSigns();
        for (SignEnum signEnum : signEnums) {
            if (exp.startsWith(signEnum.getVal()) || exp.endsWith(signEnum.getVal())) {
                return false;
            }
        }

        // 连续符号列表
        List<Triple<Integer, Integer, SignEnum>> continuousSignList =
                signIndexList.stream()
                        .filter(t -> Objects.nonNull(t.getLeft()) && (t.getMiddle() - t.getLeft() == 1))
                        .collect(Collectors.toList());

        for (Triple<Integer, Integer, SignEnum> t : continuousSignList) {
            String nextChar = String.valueOf(exp.charAt(t.getMiddle() + 1));
            SignEnum nextSign = SignEnum.match(nextChar);
            if (Objects.nonNull(nextSign) && nextSign.isNotBrackets()) {
                return false;
            }
            // 前一个符号与当前符号的组合是否是合法的符号
            String frontChar = String.valueOf(exp.charAt(t.getLeft()));
            SignEnum frontSign = SignEnum.match(frontChar);
            SignEnum combineSign = SignEnum.combineSign(frontSign, t.getRight());
            if (Objects.isNull(combineSign)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 获取公式中的变量
     *
     * @param exp 公式
     * @return 公式中的变量
     */
    public static Set<String> getExpVariables(String exp) {
        Expression compile = AviatorEvaluator.compile(exp);
        List<String> variableNames = compile.getVariableNames();
        return new HashSet<>(variableNames);
    }

    /**
     * 执行计算公式
     * <p>
     * context.expression         公式表达式
     * 变量-变量值map
     * 注意：变量值是类型敏感的,举例如下
     * String exp = "A + B";
     * Map<,> variableMap = new HashMap<>();
     * variableMap.put("A", "1");  |  variableMap.put("A", 1);
     * variableMap.put("B", "2");  |  variableMap.put("B", 2);
     * ====> 结果为 "12"            |  ====> 结果为 3
     *
     * @return 计算结果
     */
    public static Object execute(AviatorContext context) {
        try {
            Expression compiledExp = AviatorEvaluator.compile(context.getExpression(), context.isCached());
            Object result = compiledExp.execute(context.getEnv());
            return result;
        } catch (Exception ex) {
            log.error("Aviator执行器异常 context={}", JSONObject.toJSON(context), ex);
        }
        return null;
    }

    /**
     * 执行结果，返回boolean类型
     *
     * @param context 上下文对象
     * @return
     */
    public static Boolean executeBoolean(AviatorContext context) {
        Object execute = execute(context);
        if (execute == null) {
            return null;
        }
        return (Boolean) execute;
    }

    /**
     * 执行结果，返回double类型
     *
     * @param context 上下文对象
     * @return
     */
    public static Double executeDouble(AviatorContext context) {
        Object execute = execute(context);
        if (execute == null) {
            return null;
        }
        return Double.valueOf(String.valueOf(execute));
    }

    /**
     * 执行结果，返回BigDecimal类型
     *
     * @param context 上下文对象
     * @return
     */
    public static BigDecimal executeBigDecimal(AviatorContext context) {
        Object execute = execute(context);
        if (execute == null) {
            return null;
        }
        return new BigDecimal(String.valueOf(execute));
    }

    /**
     * 执行结果，返回String类型
     *
     * @param context 上下文对象
     * @return
     */
    public static String executeString(AviatorContext context) {
        Object execute = execute(context);
        if (execute == null) {
            return null;
        }
        return String.valueOf(execute);
    }

}
